package analyses;



import java.awt.Color;

public class DepthAnalyser2D {
	public double[][] depth;
	public Object[][] distribution;
	double dOrth=1;
	double dDiag=1.41;
	public static double[][] getDepth(double[][] start) {
		double[][] depth = new double[start.length][start[0].length];
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
				depth[x][y]=start[x][y];
			}
		}
		int w=depth.length;
		int h=depth[0].length;
		int nX,nY;
		double nDist;
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
					//System.out.println(x+"|"+y);
					nDist=depth[x][y]+1;
					nX=x;
					nY=y+1;
					if (nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nX=x+1;
					nY=y;
					if (nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nDist=depth[x][y]+1.4;
					nX=x+1;
					nY=y+1;
					if (nX<w&&nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nX=x+1;
					nY=y-1;
					if (nY>=0&&nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
			}
		}
		
		for (int x = depth.length-1; x >=0; x--) {
			for (int y = depth[x].length-1; y >=0; y--) {
					nDist=depth[x][y]+1;
					nX=x-1;
					nY=y;
					if (nX>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nX=x;
					nY=y-1;
					if (nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nDist=depth[x][y]+1.4;
					nX=x-1;
					nY=y-1;
					if (nX>=0&&nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nX=x-1;
					nY=y+1;
					if (nX>=0&&nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
			}
		}

		return depth;
	}
	
	public static float[] getOffset(float[]start,int nX,int nY,float offset) {
		int bim=(int)((offset))+1;
		float[] offsets = new float[start.length];
		float nDist;
		for (int i = 0; i < start.length; i++) {
			offsets[i]=start[i];
		}
		for (int i = 0; i < start.length; i++) {
			int cX = (int) (i * 1f / nY);
			int cY = i % nY;
			if (start[i] == 0) {
				int x1 = Math.max(cX - bim, 0);
				int y1 = Math.max(cY - bim, 0);
				int x2 = Math.min(cX + bim, nX);
				int y2 = Math.min(cY + bim, nY);
				for (int x = x1; x < x2; x++) {
					for (int y = y1; y < y2; y++) {
						if (x != cX || y != cY) {
							float dX = cX - x;
							float dY = cY - y;
							float d = (float) Math.sqrt(dX * dX + dY * dY);
							if (d<offset){
							int cIndex = x * nY + y;
							
							if (offsets[cIndex] > d) {
								offsets[cIndex] = d;
							}
							}
						}
					}
				}
			}
		}
		return offsets;
	}
	public static float[][] getDepth(float[][] start) {
		float[][] depth = new float[start.length][start[0].length];
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
				depth[x][y]=start[x][y];
			}
		}
		int w=depth.length;
		int h=depth[0].length;
		int nX,nY;
		float nDist;
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
					//System.out.println(x+"|"+y);
					nDist=depth[x][y]+1;
					nX=x;
					nY=y+1;
					if (nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nX=x+1;
					nY=y;
					if (nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nDist=depth[x][y]+1.41f;
					nX=x+1;
					nY=y+1;
					if (nX<w&&nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nX=x+1;
					nY=y-1;
					if (nY>=0&&nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
			}
		}
		
		for (int x = depth.length-1; x >=0; x--) {
			for (int y = depth[x].length-1; y >=0; y--) {
					nDist=depth[x][y]+1;
					nX=x-1;
					nY=y;
					if (nX>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nX=x;
					nY=y-1;
					if (nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nDist=depth[x][y]+1.41f;
					nX=x-1;
					nY=y-1;
					if (nX>=0&&nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
					nX=x-1;
					nY=y+1;
					if (nX>=0&&nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
					}
			}
		}

		return depth;
	}
	public static Color[][] getDepthWithColor(Color[][] start) {
		Color[][] result=new Color[start.length][start[0].length];
		double maxDist=start.length+start[0].length;
		double[][] depth = new double[start.length][start[0].length];
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
				
				if (start[x][y]!=null){
					depth[x][y]=0;
					result[x][y]=start[x][y];
				}
				else {
					depth[x][y]=maxDist;
				}
			}
		}
		
		int w=depth.length;
		int h=depth[0].length;
		int nX,nY;
		double nDist;
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
					//System.out.println(x+"|"+y);
					nDist=depth[x][y]+1;
					nX=x;
					nY=y+1;
					Color c=result[x][y];
					if (nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						result[nX][nY]=c;
					}
					nX=x+1;
					nY=y;
					if (nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						result[nX][nY]=c;
					}
					nDist=depth[x][y]+1.4;
					nX=x+1;
					nY=y+1;
					if (nX<w&&nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						result[nX][nY]=c;
					}
					nX=x+1;
					nY=y-1;
					if (nY>=0&&nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						result[nX][nY]=c;
					}
			}
		}
		
		for (int x = depth.length-1; x >=0; x--) {
			for (int y = depth[x].length-1; y >=0; y--) {
					nDist=depth[x][y]+1;
					nX=x-1;
					nY=y;
					Color c=result[x][y];
					if (nX>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						result[nX][nY]=c;
					}
					nX=x;
					nY=y-1;
					if (nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						result[nX][nY]=c;
					}
					nDist=depth[x][y]+1.4;
					nX=x-1;
					nY=y-1;
					if (nX>=0&&nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						result[nX][nY]=c;
					}
					nX=x-1;
					nY=y+1;
					if (nX>=0&&nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						result[nX][nY]=c;
					}
			}
		}

		return result;
	}
	public void calculateObjectDepth(Object[][] start) {
		calculateObjectDepth(start,null);
	}
	public void calculateObjectDepthOrtho(Object[][] start) {
		calculateObjectDepthOrtho(start,null);
	}
	public void calculateObjectDepth(Object[][] start,boolean[][]grow) {
		distribution=new Object[start.length][start[0].length];
		double maxDist=start.length+start[0].length;
		depth = new double[start.length][start[0].length];
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
				
				if (start[x][y]!=null){
					depth[x][y]=0;
					distribution[x][y]=start[x][y];
				}
				else {
					depth[x][y]=maxDist;
				}
			}
		}
		
		int w=depth.length;
		int h=depth[0].length;
		int nX,nY;
		double nDist;
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
					//System.out.println(x+"|"+y);
					nDist=depth[x][y]+dOrth;
					nX=x;
					nY=y+1;
					Object c=distribution[x][y];
					if (grow==null||grow[x][y]){
					if (nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					nX=x+1;
					nY=y;
					if (nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					nDist=depth[x][y]+dDiag;
					nX=x+1;
					nY=y+1;
					if (nX<w&&nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					nX=x+1;
					nY=y-1;
					if (nY>=0&&nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					}
			}
		}
		
		for (int x = depth.length-1; x >=0; x--) {
			for (int y = depth[x].length-1; y >=0; y--) {
					nDist=depth[x][y]+dOrth;
					nX=x-1;
					nY=y;
					Object c=distribution[x][y];
					if (grow==null||grow[x][y]){
					if (nX>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					nX=x;
					nY=y-1;
					if (nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					nDist=depth[x][y]+dDiag;
					nX=x-1;
					nY=y-1;
					if (nX>=0&&nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					nX=x-1;
					nY=y+1;
					if (nX>=0&&nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					}
			}
		}
	}
	public void calculateObjectDepthOrtho(Object[][] start,boolean[][]grow) {
		distribution=new Object[start.length][start[0].length];
		double maxDist=start.length+start[0].length;
		depth = new double[start.length][start[0].length];
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
				
				if (start[x][y]!=null){
					depth[x][y]=0;
					distribution[x][y]=start[x][y];
				}
				else {
					depth[x][y]=maxDist;
				}
			}
		}
		
		int w=depth.length;
		int h=depth[0].length;
		int nX,nY;
		double nDist;
		for (int x = 0; x < depth.length; x++) {
			for (int y = 0; y < depth[x].length; y++) {
					//System.out.println(x+"|"+y);
					nDist=depth[x][y]+dOrth;
					nX=x;
					nY=y+1;
					Object c=distribution[x][y];
					if (grow==null||grow[x][y]){
					if (nY<h&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					nX=x+1;
					nY=y;
					if (nX<w&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
				
					}
			}
		}
		
		for (int x = depth.length-1; x >=0; x--) {
			for (int y = depth[x].length-1; y >=0; y--) {
					nDist=depth[x][y]+dOrth;
					nX=x-1;
					nY=y;
					Object c=distribution[x][y];
					if (grow==null||grow[x][y]){
					if (nX>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					nX=x;
					nY=y-1;
					if (nY>=0&&depth[nX][nY]>nDist){
						depth[nX][nY]=nDist;
						distribution[nX][nY]=c;
					}
					
					}
			}
		}
	}
	
	public double[][] getDepth() {
		return depth;
	}
	public void setDepth(double[][] depth) {
		this.depth = depth;
	}
	public Object[][] getDistribution() {
		return distribution;
	}
	public void setDistribution(Object[][] distribution) {
		this.distribution = distribution;
	}
	
	public static void inverse(float[] values){
		for(int i = 0; i < values.length/2; i++)
		{
		    float temp = values[i];
		    values[i] = values[values.length - i - 1];
		    values[values.length - i - 1] = temp;
		}
	}
	
	public static void calculateDepth(float[] result,int nX, int nY,  float d1,
			float d2) {
		
		calculateDepthOneWay(result,nX,nY,d1,d2);
		inverse(result);
		calculateDepthOneWay(result,nX,nY,d1,d2);
		inverse(result);
	}
	public static void calculateDepthOneWay(float[] result,int nX,int nY,float d1,float d2){
		int index=0;
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
					float cDepth = result[index];
					// in dieser Ebene
					if (y < nY - 1)
						result[index+1]=Math.min(cDepth + d1, result[index+1]);
					if (x < nX - 1)
						result[index+nY]=Math.min(cDepth + d1, result[index+nY]);
					if (x < nX - 1 && y < nY - 1)
						result[ index+nY+1]=Math.min(cDepth + d2, result[index+nY+1]);
					if (x < nX - 1 && y > 0)
						result[  index+nY-1]=Math.min(cDepth + d2, result[ index+nY-1]);
					index++;
				}
		}
	}
}


